﻿using System;
using System.Collections.Generic;
using System.Text;

namespace FinalProjectLogicLayer
{
    public abstract class FeatureExtraction
    {
        private static System.Windows.Forms.ProgressBar programProgresBar = null;
        private static System.Windows.Forms.Label toolStripStatusLabel = null;
        private static int alphaBetSize = 256; //Default ABC size

        public static int AlphaBetSize
        {
            get { return FeatureExtraction.alphaBetSize; }
            set { FeatureExtraction.alphaBetSize = value; }
        }
        public static System.Windows.Forms.Label ToolStripStatusLabel
        {
            get { return FeatureExtraction.toolStripStatusLabel; }
            set { FeatureExtraction.toolStripStatusLabel = value; }
        }
        public static System.Windows.Forms.ProgressBar ProgresBar
        {
            get { return FeatureExtraction.programProgresBar; }
            set { FeatureExtraction.programProgresBar = value; }
        }

        /// <summary>
        /// This method converts a text file to a String (SUPORTTING ONLY ASCII FOR NOW)
        /// </summary>
        /// <param name="path">The path of the text file to convert</param>
        /// <returns>String containing the text</returns>
        public static String fileToString(String path)
        {
            String fileAsString;
            if (path == null) return "";
           // if (path == "") return "";

            fileAsString = fileToString(path, 1)[0];
           
            return fileAsString;
            
        }
        /// <summary>
        /// This mask is alligned to the right and fits the size of the alphaBet
        /// </summary>
        /// <param name="maskSize"></param>
        /// <returns></returns>
        public static int createCharMask(int AbcSizeInCharacters,int letterSizeInBits)
        {
            int maskOnesSize = (int)Math.Log(AbcSizeInCharacters, 2);
            int maskOnes = AbcSizeInCharacters - 1;
            int mask = (maskOnes << (letterSizeInBits - maskOnesSize));
            return mask;
        }
        /// <summary>
        /// This method conerts the file to a string
        /// </summary>
        /// <param name="path">the path of the file to convert</param>
        /// <param name="parts">to how many part devide this file</param>
        /// <returns>Returns file contents as string</returns>
        public static String reduceAlphaBet(String input, int characterdSizeInBits)
        {
            return reduceAlphaBet(input, characterdSizeInBits,null);
        }
        public static String reduceAlphaBet(String input,int characterdSizeInBits,String name)
        {
            char[] outPut;
            int oldProgressBarValue = 0;
            int oldProgressBarMax = 0;
            String oldLabelValue = "";
            if (name == null) name = "";
            if (programProgresBar != null)
            {
                oldProgressBarValue = programProgresBar.Value;
                oldProgressBarMax = programProgresBar.Maximum;
                programProgresBar.Maximum = input.Length/500;
                programProgresBar.Value = 0;
                programProgresBar.ForeColor = System.Drawing.Color.Blue;
                if (toolStripStatusLabel != null)
                {
                    oldLabelValue = toolStripStatusLabel.Text;
                    toolStripStatusLabel.Text = "REDUCING ALPHA BET SIZE: " + name;
                    toolStripStatusLabel.Update();
                }
            }
            int maskOnesSize = (int)Math.Log(FeatureExtraction.AlphaBetSize, 2);
            outPut = input.ToCharArray();
            int shitAmount = characterdSizeInBits - maskOnesSize;
            int avoidNullCharactesInAlphaBet = 0;
            if (shitAmount != 0)
                avoidNullCharactesInAlphaBet += 2; //after reducing the alpha bet size the first characters are the null symbol
            for (int i = 0; i < input.Length; i++)
            {
                //outPut[i] = (char)(((outPut[i]) >> shitAmount) + avoidNullCharactesInAlphaBet);
                outPut[i] = (char)(((outPut[i])%alphaBetSize ) + avoidNullCharactesInAlphaBet);
                if (programProgresBar != null && (i%500 == 0))
                {
                    programProgresBar.PerformStep();
                    programProgresBar.Update();
                }
                System.Windows.Forms.Application.DoEvents();
            }
            if (programProgresBar != null)
            {
                programProgresBar.Maximum = oldProgressBarMax;
                programProgresBar.Value = oldProgressBarValue;
                programProgresBar.Update();
                if (toolStripStatusLabel != null)
                {
                    toolStripStatusLabel.Text = oldLabelValue;
                    toolStripStatusLabel.Update();
                }
            }
            //System.Console.WriteLine(outPut);

            return new String(outPut);
        }
        //REDUCE ALPHABET USING SHIFTMETHOD
        public static String reduceAlphaBet2(String input, int characterdSizeInBits, String name)
        {
            char[] outPut;
            int oldProgressBarValue = 0;
            String oldLabelValue = "";
            if (name == null) name = "";
            if (programProgresBar != null)
            {
                oldProgressBarValue = programProgresBar.Value;
                programProgresBar.Maximum = input.Length / 500;
                programProgresBar.Value = 0;
                programProgresBar.ForeColor = System.Drawing.Color.Blue;
                if (toolStripStatusLabel != null)
                {
                    oldLabelValue = toolStripStatusLabel.Text;
                    toolStripStatusLabel.Text = "REDUCING ALPHA BET SIZE: " + name;
                    toolStripStatusLabel.Update();
                }
            }
            int maskOnesSize = (int)Math.Log(FeatureExtraction.AlphaBetSize, 2);
            outPut = input.ToCharArray();
            int shitAmount = characterdSizeInBits - maskOnesSize;
            int avoidNullCharactesInAlphaBet = 0;
            if (shitAmount != 0)
                avoidNullCharactesInAlphaBet += 2; //after reducing the alpha bet size the first characters are the null symbol
            for (int i = 0; i < input.Length; i++)
            {
                outPut[i] = (char)(((outPut[i]) >> shitAmount) + avoidNullCharactesInAlphaBet);
                //outPut[i] = (char)(((outPut[i]) % alphaBetSize) + avoidNullCharactesInAlphaBet);
                if (programProgresBar != null && (i % 500 == 0))
                {
                    programProgresBar.PerformStep();
                    programProgresBar.Update();
                }
                System.Windows.Forms.Application.DoEvents();
            }
            if (programProgresBar != null)
            {
                programProgresBar.Value = oldProgressBarValue;
                programProgresBar.Update();
                if (toolStripStatusLabel != null)
                {
                    toolStripStatusLabel.Text = oldLabelValue;
                    toolStripStatusLabel.Update();
                }
            }
            //System.Console.WriteLine(outPut);

            return new String(outPut);
        }
        
        public static String[] fileToString(String path, int parts)
        {
            if (parts <= 0)
                parts=1;
            int characterSizeInBits = 8; //TODO: calcualte according to input;
            String[] result = new String[parts];
            String tempResult = System.IO.File.ReadAllText(path, new ASCIIEncoding());
            /*  REDUCE ALPHA BET */
            //if (FeatureExtraction.AlphaBetSize!=256)
                tempResult = FeatureExtraction.reduceAlphaBet(tempResult,characterSizeInBits,path.Substring(path.LastIndexOf('\\')+1));
            /*********************/
            int partSize = tempResult.Length / parts;
            for (int i = 0; i < parts; i++)
                result[i] = tempResult.Substring(i * partSize, partSize);
            return result;
        }
        /// <summary>
        /// This method calculates the feature vectors of the input files according to the parameters
        /// </summary>
        /// <param name="fileList">The list of all the files that we want to extract their features</param>
        /// <param name="dictionary">The dictionary that the files will be extracted by</param>
        /// <param name="errorsAllowed">The mesurmant of approximate matching while extracting the features     </param>
        public static int calculate(FileList fileList, Dictionary dictionary,int errorsAllowed)
        {
            String[] tempFileStrings;
            int dictionaryLength = dictionary.DictionarySize;
            // REMOVED FOR MULTITHREAD TEST
            //double totalCSVMatches = 0;
            //double[] tempCSV;
            //double checkSum = 0;
            //END OF REMOVED VARIABLES
            if (programProgresBar != null)
            {
                int numberOfActualFilesInProcess = 0; //This variable will hold the number all the files (and subfiles) in the process 
                foreach (FileInfo file in fileList) //for all the files in the list
                {
                    numberOfActualFilesInProcess += file.DivisionParameter;
                }
                programProgresBar.Maximum = numberOfActualFilesInProcess;
                programProgresBar.Step = 1;
                programProgresBar.Value = 0;
                if (toolStripStatusLabel != null)
                {
                    toolStripStatusLabel.Text = "Calculating CSV's OVERALL Progress";
                    toolStripStatusLabel.Update();
                }
            }
            foreach (FileInfo file in fileList) //for all the files in the list
            {
                if ((file.DictionaryID == dictionary.ID) && (file.FeatureVectors.Count == file.DivisionParameter) && (fileList.ValidCSVs)) //if file features where allready extracted //TODO: avoid calculating twice
                    continue;
                file.DictionaryID = dictionary.ID; //set the dictionary serial number property of the current file
                tempFileStrings = fileToString(file.FilePath, file.DivisionParameter); //get file as array of Strings according to the division parameter
                file.FeatureVectors.Clear();
                int partNum = 0;
                foreach (String curFileString in tempFileStrings)//for each part of the text of the file
                {
                    tempCSV = new double[dictionaryLength];
                   
                    //UPDATE GUI LABEL IF EXSISTS
                    if (toolStripStatusLabel != null)
                    {
                        String fileName = file.FilePath.Substring(file.FilePath.LastIndexOf('\\') + 1);
                        if (tempFileStrings.Length > 0)
                        {
                            fileName += " Part :" + (++partNum) + " of " + tempFileStrings.Length;
                        }
                        toolStripStatusLabel.Text = "Over all csv's calculation Progress. NOW  Calculating: " + fileName ;
                        toolStripStatusLabel.Update();
                    }

                    // ************VVVVVV multiThread option *****calculate tempCSV************
                    int processorCount = Environment.ProcessorCount; //get number of processors
                    FeatureExtraction.dictionaryWords = dictionary.DictionaryWords;
                    FeatureExtraction.curFileString = curFileString;
                    FeatureExtraction.errorsAllowed = errorsAllowed;
                    FeatureExtraction.totalCSVMatches = 0;
                    FeatureExtraction.dictionaryWordIndex = 0;
                    System.Threading.Thread[] stringMatchingThreads = new System.Threading.Thread[processorCount];
                    for (int i = 0; i < stringMatchingThreads.Length; i++)
                    {
                        stringMatchingThreads[i] = new System.Threading.Thread(stringMatchingThread);
                        stringMatchingThreads[i].Name = "stringMatchingThread :" + (i+1);
                        stringMatchingThreads[i].Start();
                    }
                    for (int i = 0; i < stringMatchingThreads.Length; i++)
                    {
                        stringMatchingThreads[i].Join();
                    }
                    if (totalCSVMatches != 0)
                    {
                        for (int i = 0; i < dictionaryWords.Length; i++) //calculate the probability
                        {
                            tempCSV[i] = tempCSV[i] / totalCSVMatches;
                        }
                    }
                    // ******************END OF MULTITHREADING CODE ************
                    //totalCSVMatches = 0; //*****REMOVED FOR MULTITHREADING TEST
                    //for (int i = 0; i < dictionaryLength; i++) //search for matchs for all dictionary words
                    //{
                    //    tempCSV[i] = StringMatching.search(curFileString, dictionary.DictionaryWords[i], errorsAllowed);
                    //    totalCSVMatches += tempCSV[i];
                    //}

                    //if (totalCSVMatches != 0)
                    //{
                    //    checkSum = 0;
                    //    for (int i = 0; i < dictionaryLength; i++) //calculate the probability
                    //    {
                    //        tempCSV[i] = tempCSV[i] / totalCSVMatches;
                    //        checkSum += tempCSV[i];
                    //    }
                    //}
                    // ************^^^^^^^ multiThread option *****************
                    file.FeatureVectors.Add(tempCSV); //add the feature vectore to list
                    
                    if (programProgresBar != null)
                    {
                        programProgresBar.PerformStep();
                        programProgresBar.Update();
                        System.Windows.Forms.Application.DoEvents();
                    }
                }
            }
            fileList.ValidCSVs = true;
            if (toolStripStatusLabel != null)
            {
                toolStripStatusLabel.Text = "CSV's calulation done";
                toolStripStatusLabel.Update();
            }
            return 0;
        }

        /************* MULTITHREADING TEST **********/
        private static int dictionaryWordIndex;
        private static object locker = new object();
        private static String curFileString;
        private static double[] tempCSV;
        private static String[] dictionaryWords;
        private static int errorsAllowed;
        private static double totalCSVMatches = 0;
        private static void stringMatchingThread()
        {
            int i;
            bool notFinished = true;
            while (notFinished)
            {
                lock (locker) //Critical section
                {
                    i = dictionaryWordIndex++;
                    if (i >= dictionaryWords.Length)
                    {
                        notFinished = false;
                        break;
                    }

                }
                tempCSV[i] = StringMatching.search(curFileString, dictionaryWords[i], errorsAllowed);
                lock (locker) //Critical section
                {
                    totalCSVMatches += tempCSV[i]; //total CSV matches
                }
            }

        }
        public static int calculateNoMultiThreading(FileList fileList, Dictionary dictionary, int errorsAllowed)
        {
            String[] tempFileStrings;
            int dictionaryLength = dictionary.DictionarySize;
            double totalCSVMatches = 0;
            double[] tempCSV;
            double checkSum = 0;
            if (programProgresBar != null)
            {
                int numberOfActualFilesInProcess = 0; //This variable will hold the number all the files (and subfiles) in the process 
                foreach (FileInfo file in fileList) //for all the files in the list
                {
                    numberOfActualFilesInProcess += file.DivisionParameter;
                }
                programProgresBar.Maximum = numberOfActualFilesInProcess;
                programProgresBar.Step = 1;
                programProgresBar.Value = 0;
                if (toolStripStatusLabel != null)
                {
                    toolStripStatusLabel.Text = "Calculating CSV's OVERALL Progress";
                    toolStripStatusLabel.Update();
                }
            }
            foreach (FileInfo file in fileList) //for all the files in the list
            {
                if ((file.DictionaryID == dictionary.ID) && (file.FeatureVectors.Count == file.DivisionParameter) && (fileList.ValidCSVs)) //if file features where allready extracted //TODO: avoid calculating twice
                    continue;
                file.DictionaryID = dictionary.ID; //set the dictionary serial number property of the current file
                tempFileStrings = fileToString(file.FilePath, file.DivisionParameter); //get file as array of Strings according to the division parameter
                file.FeatureVectors.Clear();
                int partNum = 0;
                foreach (String curFileString in tempFileStrings)//for each part of the text of the file
                {
                    tempCSV = new double[dictionaryLength];

                    //UPDATE GUI LABEL IF EXSISTS
                    if (toolStripStatusLabel != null)
                    {
                        String fileName = file.FilePath.Substring(file.FilePath.LastIndexOf('\\') + 1);
                        if (tempFileStrings.Length > 0)
                        {
                            fileName += " Part :" + (++partNum) + " of " + tempFileStrings.Length;
                        }
                        toolStripStatusLabel.Text = "Over all csv's calculation Progress. NOW  Calculating: " + fileName;
                        toolStripStatusLabel.Update();
                    }

                // *****calculate tempCSV************
 
                  
                    totalCSVMatches = 0; 
                    for (int i = 0; i < dictionaryLength; i++) //search for matchs for all dictionary words
                    {
                        tempCSV[i] = StringMatching.search(curFileString, dictionary.DictionaryWords[i], errorsAllowed);
                        totalCSVMatches += tempCSV[i];
                    }

                    if (totalCSVMatches != 0)
                    {
                        checkSum = 0;
                        for (int i = 0; i < dictionaryLength; i++) //calculate the probability
                        {
                            tempCSV[i] = tempCSV[i] / totalCSVMatches;
                            checkSum += tempCSV[i];
                        }
                    }
                  
                    file.FeatureVectors.Add(tempCSV); //add the feature vectore to list

                    if (programProgresBar != null)
                    {
                        programProgresBar.PerformStep();
                        programProgresBar.Update();
                        System.Windows.Forms.Application.DoEvents();
                    }
                }
            }
            fileList.ValidCSVs = true;
            if (toolStripStatusLabel != null)
            {
                toolStripStatusLabel.Text = "CSV's calulation done";
                toolStripStatusLabel.Update();
            }
            return 0;
        }

    }
}
